package zheezes.eisp.collect.model.impl.gbw;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import org.apache.commons.lang.ArrayUtils;
import org.apache.log4j.Logger;

import zheezes.eisp.collect.model.CollectTaskHandler;
import zheezes.eisp.data.orm.CollectTask;
import zheezes.eisp.data.orm.Concentrator;
import zheezes.eisp.data.orm.FrozenData;
import zheezes.eisp.data.orm.FrozenType;
import zheezes.eisp.data.orm.Summator;
import zheezes.protocol.Frame;
import zheezes.protocol.impl.gbw.DataUnit;
import zheezes.protocol.impl.gbw.GbwConAddr;
import zheezes.protocol.impl.gbw.GbwFrame;
import zheezes.protocol.impl.gbw.afn0d.AbstractAfn0DDownCurveDataDataUnit;
import zheezes.protocol.impl.gbw.afn0d.AbstractAfn0DUpActivePowerCurveDataUnit;
import zheezes.protocol.impl.gbw.afn0d.AbstractAfn0DUpCurrentCurveDataUnit;
import zheezes.protocol.impl.gbw.afn0d.AbstractAfn0DUpEnergyCurveDataUnit;
import zheezes.protocol.impl.gbw.afn0d.AbstractAfn0DUpEnergyShowCurveDataUnit;
import zheezes.protocol.impl.gbw.afn0d.AbstractAfn0DUpPowerFactorCurveDataUnit;
import zheezes.protocol.impl.gbw.afn0d.AbstractAfn0DUpReactivePowerCurveDataUnit;
import zheezes.protocol.impl.gbw.afn0d.AbstractAfn0DUpVoltageCurveDataUnit;
import zheezes.protocol.impl.gbw.define.Density;
import zheezes.protocol.impl.gbw.format.DataForm05;
import zheezes.protocol.impl.gbw.format.DataForm06;
import zheezes.protocol.impl.gbw.format.DataForm07;
import zheezes.protocol.impl.gbw.format.DataForm09;
import zheezes.protocol.impl.gbw.format.DataForm11;
import zheezes.protocol.impl.gbw.format.DataForm13;
import zheezes.protocol.impl.gbw.format.DataForm15;
import zheezes.socket.Transput;


public class GbwSummatorInstantValueCollectTaskHandler extends CollectTaskHandler {
	private static Logger logger = Logger.getLogger(GbwSummatorInstantValueCollectTaskHandler.class);
	
	private static final int[] COLLECT_SET = {
		FrozenType.ACTIVE_POWER_CURVE,
		FrozenType.A_PHASE_ACTIVE_POWER_CURVE,
		FrozenType.B_PHASE_ACTIVE_POWER_CURVE,
		FrozenType.C_PHASE_ACTIVE_POWER_CURVE,
			
		FrozenType.REACTIVE_POWER_CURVE,
		FrozenType.A_PHASE_REACTIVE_POWER_CURVE,
		FrozenType.B_PHASE_REACTIVE_POWER_CURVE,
		FrozenType.C_PHASE_REACTIVE_POWER_CURVE,
			
		FrozenType.A_PHASE_VOLTAGE_CURVE,
		FrozenType.B_PHASE_VOLTAGE_CURVE,
		FrozenType.C_PHASE_VOLTAGE_CURVE,
			
		FrozenType.A_PHASE_CURRENT_CURVE,
		FrozenType.B_PHASE_CURRENT_CURVE,
		FrozenType.C_PHASE_CURRENT_CURVE,
		FrozenType.NULL_CURRENT_CURVE,
			
		FrozenType.POSITIVE_ACTIVE_ENERGY,
		FrozenType.POSITIVE_REACTIVE_ENERGY,
		FrozenType.NEGATIVE_ACTIVE_ENERGY,
		FrozenType.NEGATIVE_REACTIVE_ENERGY,
			
		FrozenType.POSITIVE_ACTIVE_ENERGY_SHOW,
		FrozenType.POSITIVE_REACTIVE_ENERGY_SHOW,
		FrozenType.NEGATIVE_ACTIVE_ENERGY_SHOW,
		FrozenType.NEGATIVE_REACTIVE_ENERGY_SHOW,
			
		FrozenType.POWER_FACTOR,
		FrozenType.A_PHASE_POWER_FACTOR,
		FrozenType.B_PHASE_POWER_FACTOR,
		FrozenType.C_PHASE_POWER_FACTOR
	};
	
	private boolean exist(int type, List<FrozenType> colls) {
		for(FrozenType fd : colls) {
			if(type == fd.getFn()) {
				return true;
			}
		}
		return false;
	}
	
	public int[] getCollSet(Calendar cal) {
		List<Integer> list = new ArrayList<Integer>();
		// dao should not to call many times
		List<FrozenType> cset = dataAccessor.queryToCollTypes(cal.getTime());
		logger.debug(String.format("there is %d value to collect", cset.size()));
		try{
		for (int i = 0; i < COLLECT_SET.length; i++) {
			int item = COLLECT_SET[i];
			if(exist(item, cset)) {
				list.add(item);
			}
		}
		} catch(Exception e) {
			e.printStackTrace();
		}
		return ArrayUtils.toPrimitive(list.toArray(new Integer[0]));
	}
	
	public short getFn(int type) {
		return (short) type;
	}
	
	public int getType(short fn) {
		return fn;
	}
	
	public GbwFrame getFrame(Concentrator con, int type, Calendar stime, byte density, byte amount) {
		GbwFrame frame = new GbwFrame();
		frame.setDirection(GbwFrame.DIRECTION_DOWN);
		frame.setPrm(GbwFrame.PRM_STARTUP);
		frame.setConcentrator(new GbwConAddr(con.getLogicAddr()));
		frame.setAfn((byte) 0x0D);
		AbstractAfn0DDownCurveDataDataUnit du = (AbstractAfn0DDownCurveDataDataUnit) DataUnit.getDataUnit((byte) 0x0D, getFn(type), 0);
		DataForm15 df = new DataForm15();
		df.setCal(stime);
		du.setStime(df);
		du.setDensity(density);
		du.setAmount(amount);
		frame.addDataUnit(du);
		
		return frame;
	}
	
	public GbwFrame[] collect(Concentrator con, Calendar cal, int type, int interval) {
		final int cell = 24; // every packet has 24 points
		List<GbwFrame> list = new ArrayList<GbwFrame>();
		int times = 1;
		if(interval < 60) {
			times = 60 / interval;
		}
		byte density = (byte) Density.getType(interval);
		Calendar stime = (Calendar) cal.clone();
		stime.set(Calendar.MINUTE, 0);
		stime.set(Calendar.HOUR, 0);
		
		for (int i = 0; i < times && !terminated; i++) {
			GbwFrame frame = getFrame(con, type, stime, density, (byte) cell);
			
			// mailbox.clear();
			Transput transput;
			if ((transput = postOffice.send(frame)) != null) {
				Frame[] recv = null;
				if (transput.await() == Transput.SUCCESS && (recv = transput.getRecv()) != null) {
					// Object message = null;
					for(Frame message : recv) {
					// while((message = mailbox.get()) != null) {
						GbwFrame frm = (GbwFrame) message;
						if (frm.getAfn() == frm.getAfn()) {
							list.add(frm);
							stime.add(Calendar.MINUTE, cell * interval);
							continue; // success, to continue
						}	
					}
				}
			} else {
				return null; // if any error
			}
		}
		assert list.size() == times;
		return list.toArray(new GbwFrame[0]);
	}
	
	public void run() {
		Concentrator con = dataAccessor.getConById(task.getConId());
		Calendar cal = Calendar.getInstance();
		cal.setTime(task.getDataDate());
		Summator sm = dataAccessor.getSummatorByCon(task.getConId());
		int[] cset = getCollSet(cal);
		logger.debug(String.format("there is %d will be collect", cset.length));
		
		int interval = 15; // minutes		
		
		int count = 0;
		for (int i = 0; i < cset.length && !terminated; i++) {
			int type = cset[i];
			GbwFrame[] frames = collect(con, cal, type, interval);

			if (frames != null) {
				if(save(frames, sm.getMpId()) == 0) {
					count++;
				}
			}
		}
		
		if(count == cset.length) {
			task.setStatus(CollectTask.STATUS_FINISH);
		}
		task.setLastRunDate(Calendar.getInstance().getTime());
		dataAccessor.updateCollectTask(task);
	}
	
	public int save(GbwFrame[] frames, int mpId) {
		dataAccessor.beginTransaction();
		FrozenData fd = new FrozenData();
		fd.setMpId(mpId);
		Calendar cal = Calendar.getInstance();
		int interval = 0;
		for (GbwFrame frame : frames) {
			if (frame.getAfn() == 0x0D) {
				for (DataUnit du : frame.getDataUnits()) {
					if (du instanceof AbstractAfn0DUpActivePowerCurveDataUnit) {
						AbstractAfn0DUpActivePowerCurveDataUnit unit = (AbstractAfn0DUpActivePowerCurveDataUnit) du;
						cal = (Calendar) unit.getStime().getCal().clone();
						interval = Density.getInterval(unit.getDensity());
						fd.setType(getType(unit.getId().getFn()));
						for (DataForm09 df : unit.getData()) {
							fd.setDate(cal.getTime());
							fd.setValue(df.getValue());
							dataAccessor.saveFrozenData(fd);
							cal.add(Calendar.MINUTE, interval);
						}
					} else if (du instanceof AbstractAfn0DUpCurrentCurveDataUnit) {
						AbstractAfn0DUpCurrentCurveDataUnit unit = (AbstractAfn0DUpCurrentCurveDataUnit) du;
						cal = (Calendar) unit.getStime().getCal().clone();
						interval = Density.getInterval(unit.getDensity());
						fd.setType(getType(unit.getId().getFn()));
						for (DataForm06 df : unit.getData()) {
							fd.setDate(cal.getTime());
							fd.setValue(df.getValue());
							dataAccessor.saveFrozenData(fd);
							cal.add(Calendar.MINUTE, interval);
						}
					} else if (du instanceof AbstractAfn0DUpEnergyCurveDataUnit) {
						AbstractAfn0DUpEnergyCurveDataUnit unit = (AbstractAfn0DUpEnergyCurveDataUnit) du;
						cal = (Calendar) unit.getStime().getCal().clone();
						interval = Density.getInterval(unit.getDensity());
						fd.setType(getType(unit.getId().getFn()));
						for (DataForm13 df : unit.getData()) {
							fd.setDate(cal.getTime());
							fd.setValue(df.getValue());
							dataAccessor.saveFrozenData(fd);
							cal.add(Calendar.MINUTE, interval);
						}
					} else if (du instanceof AbstractAfn0DUpEnergyShowCurveDataUnit) {
						AbstractAfn0DUpEnergyShowCurveDataUnit unit = (AbstractAfn0DUpEnergyShowCurveDataUnit) du;
						cal = (Calendar) unit.getStime().getCal().clone();
						interval = Density.getInterval(unit.getDensity());
						fd.setType(getType(unit.getId().getFn()));
						for (DataForm11 df : unit.getData()) {
							fd.setDate(cal.getTime());
							fd.setValue(df.getValue());
							dataAccessor.saveFrozenData(fd);
							cal.add(Calendar.MINUTE, interval);
						}
					} else if (du instanceof AbstractAfn0DUpPowerFactorCurveDataUnit) {
						AbstractAfn0DUpPowerFactorCurveDataUnit unit = (AbstractAfn0DUpPowerFactorCurveDataUnit) du;
						cal = (Calendar) unit.getStime().getCal().clone();
						interval = Density.getInterval(unit.getDensity());
						fd.setType(getType(unit.getId().getFn()));
						for (DataForm05 df : unit.getData()) {
							fd.setDate(cal.getTime());
							fd.setValue(df.getValue());
							dataAccessor.saveFrozenData(fd);
							cal.add(Calendar.MINUTE, interval);
						}
					} else if (du instanceof AbstractAfn0DUpReactivePowerCurveDataUnit) {
						AbstractAfn0DUpReactivePowerCurveDataUnit unit = (AbstractAfn0DUpReactivePowerCurveDataUnit) du;
						cal = (Calendar) unit.getStime().getCal().clone();
						interval = Density.getInterval(unit.getDensity());
						fd.setType(getType(unit.getId().getFn()));
						for (DataForm09 df : unit.getData()) {
							fd.setDate(cal.getTime());
							fd.setValue(df.getValue());
							dataAccessor.saveFrozenData(fd);
							cal.add(Calendar.MINUTE, interval);
						}
					} else if (du instanceof AbstractAfn0DUpVoltageCurveDataUnit) {
						AbstractAfn0DUpVoltageCurveDataUnit unit = (AbstractAfn0DUpVoltageCurveDataUnit) du;
						cal = (Calendar) unit.getStime().getCal().clone();
						interval = Density.getInterval(unit.getDensity());
						fd.setType(getType(unit.getId().getFn()));
						for (DataForm07 df : unit.getData()) {
							fd.setDate(cal.getTime());
							fd.setValue(df.getValue());
							dataAccessor.saveFrozenData(fd);
							cal.add(Calendar.MINUTE, interval);
						}
					} else {
						dataAccessor.rollback();
						logger.debug("can't read data from the frame");
						return -1;
					}
				}
			} else {
				dataAccessor.rollback();
				logger.warn("the frame received is not match");
				return -1;
			}
		}
		return dataAccessor.commit();
	}
}
